﻿using DotNetCommon.Logger;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Versioning;

namespace DotNetCommon.Extensions;
/// <summary>
/// <see cref="Assembly"/>扩展类
/// </summary>
public static class AssemblyExtensions
{
    /// <summary>
    /// 获取程序集标记的 <c>.net</c> 版本, 返回示例: ".NETCoreApp,Version=v8.0"<br/><br/>
    /// 通过读取程序集内的 <seealso cref="TargetFrameworkAttribute" /> 特性实现
    /// </summary>
    public static string GetFrameworkVersion(this Assembly assembly)
    {
        AssertUtil.NotNull(assembly);

        var targetFrameAttribute = assembly.GetCustomAttributes(true)
            .OfType<TargetFrameworkAttribute>().FirstOrDefault();
        if (targetFrameAttribute is null) return "unknown";
        return targetFrameAttribute.FrameworkName;
    }

    /// <summary>
    /// 获取程序集文件所在的目录 从 <seealso cref="Assembly.Location"/> 处获取
    /// </summary>
    public static DirectoryInfo GetAssemblyLocation(this Assembly assembly)
    {
        AssertUtil.NotNull(assembly);
        return new DirectoryInfo(Path.GetDirectoryName(assembly.Location) ?? throw new InvalidOperationException());
    }

    /// <summary>
    /// 当前程序集是否以Release模式编译 参照 <see href="http://www.hanselman.com/blog/HowToProgrammaticallyDetectIfAnAssemblyIsCompiledInDebugOrReleaseMode.aspx"/>
    /// </summary>
    public static bool IsOptimized(this Assembly assembly)
    {
        AssertUtil.NotNull(assembly);

        var attributes = assembly.GetCustomAttributes(typeof(DebuggableAttribute), false);

        if (attributes.Length == 0) return true;

        foreach (Attribute attr in attributes)
        {
            if (attr is DebuggableAttribute d)
            {
                // FYI
                // "Run time Optimizer is enabled: " + !d.IsJITOptimizerDisabled
                // "Run time Tracking is enabled: " + d.IsJITTrackingEnabled
                return !d.IsJITOptimizerDisabled;
            }
        }
        return false;
    }

    /// <summary>
    /// 当前程序集是否是32位(通过读取 *.dll 文件流判断), <seealso href="https://www.cnblogs.com/LifeDecidesHappiness/p/15711169.html"/>
    /// </summary>
    /// <remarks>注意: 以 Any CPU 编译的也会判断为true</remarks>
    public static bool Is32Bit(this Assembly assembly)
    {
        AssertUtil.NotNull(assembly);
        try
        {
            ushort architecture = 0;
            using var fStream = new FileStream(assembly.Location, FileMode.Open, FileAccess.Read);
            using var bReader = new BinaryReader(fStream);
            if (bReader.ReadUInt16() == 23117) //check the MZ signature
            {
                fStream.Seek(0x3A, SeekOrigin.Current); //seek to e_lfanew.
                fStream.Seek(bReader.ReadUInt32(), SeekOrigin.Begin); //seek to the start of the NT header.
                if (bReader.ReadUInt32() == 17744) //check the PE\0\0 signature.
                {
                    fStream.Seek(20, SeekOrigin.Current); //seek past the file header,
                    architecture = bReader.ReadUInt16(); //read the magic number of the optional header.
                }
            }
            //anyCPU & x86:267
            //x64:523
            return architecture == 267;
        }
        catch (Exception ex)
        {
            LoggerFactory.CreateLogger("DotNetCommon.Core.Extensions.AssemblyExtensions").LogError(ex, $"判断程序集({assembly?.FullName})是否是32位时出错:{ex.Message}");
            throw;
        }
    }
}